package ar.com.osdepym.agregacionafip.modelfile;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import ar.com.osdepym.agregacionafip.DateUtilities;
import ar.com.osdepym.agregacionafip.NumberUtils;
import ar.com.osdepym.agregacionafip.StringUtilities;
import ar.com.osdepym.agregacionafip.configuracion.ConfiguracionAplicacion;
import ar.com.osdepym.agregacionafip.exceptions.FormatoFechaException;
import ar.com.osdepym.agregacionafip.exceptions.ParseoArchivoException;
import ar.com.osdepym.agregacionafip.parser.CamposDeArchivo;
import ar.com.osdepym.agregacionafip.parser.Parser;

public class Transferencia implements Archivo<TransferenciasDetalle> {

	private String cabecera;
	private Resultado resultado;
	private HashMap<String, TransferenciasDetalle> detalles;
	Parser parser = new Parser();
	private String trailer;
	private List<String> trailerDetalle;

	private int detallesNoAgrupadosCantidad;

	private TransferenciasFileField parseo;

	public Transferencia(String cabecera) {
		this.trailerDetalle = new Vector<String>();

		this.resultado = new Resultado();
		this.cabecera = cabecera;
		this.detalles = new HashMap<String, TransferenciasDetalle>();
		this.parseo = new TransferenciasFileField();
	}

	@Override
	public void add(TransferenciasDetalle detalle,
			ConfiguracionAplicacion conf, OutputStreamWriter fout)
			throws IOException {

		String cuit = String.valueOf(detalle.getCuitContribuyente());
		Date periodoConf = DateUtilities.obtenerFechaByAnioMes(conf
				.getPeriodoInicio());
		Date periodoDetalle = DateUtilities.obtenerFechaByAnioMes(String
				.valueOf(detalle.getPeriodo()));

		if (conf.getCuitHabilitados().contains(cuit)
				&& DateUtilities.esMayorOigual(periodoDetalle, periodoConf)) {
			TransferenciasDetalle det = detalles.get(detalle.getKey());
			this.resultado.setControlArchivoEntrada(this.resultado
					.getControlArchivoEntrada() + detalle.getImporte());
			if (det != null) {

				det.setCuitAportante(Long.valueOf(conf.getCuilAfiliado()));
				det.setImporte(NumberUtils.sumarLong(det.getImporte(),
						detalle.getImporte()));
				det.setFechaDeTransferencia(DateUtilities.maximo(
						det.getFechaDeTransferencia(),
						detalle.getFechaDeTransferencia()));
				det.setFechaDeRecaudacion(DateUtilities.maximo(
						det.getFechaDeRecaudacion(),
						detalle.getFechaDeRecaudacion()));
				det.setNumeroObligacion(StringUtilities.maximo(
						det.getNumeroObligacion(),
						detalle.getNumeroObligacion()));
				det.setSecuenciObligacion(StringUtilities.maximo(
						det.getSecuenciObligacion(),
						detalle.getSecuenciObligacion()));
				det.setBanco(StringUtilities.maximo(det.getBanco(),
						detalle.getBanco()));
				det.setSucursalNominacion(StringUtilities.maximo(
						det.getSucursalNominacion(),
						detalle.getSucursalNominacion()));
				det.setZona(NumberUtils.maximoInteger(det.getZona(),
						detalle.getZona()));
				det.setPorcentajeReduccion(NumberUtils.maximoInteger(
						det.getPorcentajeReduccion(),
						detalle.getPorcentajeReduccion()));
				det.setPorcentajeReduccion2(NumberUtils.maximoInteger(
						det.getPorcentajeReduccion2(),
						detalle.getPorcentajeReduccion2()));
				det.setPorcentajeReduccion3(NumberUtils.maximoInteger(
						det.getPorcentajeReduccion3(),
						detalle.getPorcentajeReduccion3()));
				det.setGrupoFamiliar(NumberUtils.maximoInteger(
						det.getGrupoFamiliar(), detalle.getGrupoFamiliar()));
				det.setTipoPago(StringUtilities.maximo(det.getTipoPago(),
						detalle.getTipoPago()));
				det.setMarcaApropiacion(StringUtilities.maximo(
						det.getMarcaApropiacion(),
						detalle.getMarcaApropiacion()));

			} else {
				detalle.setCuitAportante(Long.valueOf(conf.getCuilAfiliado()));
				detalles.put(detalle.getKey(), detalle);
			}
		} else {

			detallesNoAgrupadosCantidad++;
			fout.write(parser.buildFila(detalle, this.obtenerParseoDetalle())
					+ "\n");
		}

	}

	@Override
	public CamposDeArchivo obtenerParseoDetalle() {

		return this.parseo.obtenerConfiguracionDetalle();
	}

	@Override
	public Resultado procesar(BufferedReader fin, ConfiguracionAplicacion conf,
			OutputStreamWriter fout) {

		Parser parser = new Parser();
		TransferenciasDetalle detalle;

		try {

			fout.write(this.cabecera + "\n");

			while (fin.ready()) {
				detalle = new TransferenciasDetalle();
				String lineaDeArchivo = fin.readLine();
				if (!this.esTrailer(lineaDeArchivo)) {
					if (!this.esTrailerDetalle(lineaDeArchivo)) {
						parser.parserFila(lineaDeArchivo,
								this.obtenerParseoDetalle(), detalle);

						this.add(detalle, conf, fout);
					} else {
						this.trailerDetalle.add(lineaDeArchivo);
					}
				} else {
					this.trailer = lineaDeArchivo;
				}
			}

			for (TransferenciasDetalle transferenciaDetalle : detalles.values()) {
				fout.write(parser.buildFila(transferenciaDetalle,
						this.obtenerParseoDetalle())
						+ "\n");
			}

			for (TransferenciasDetalle detalle2 : this.detalles.values()) {
				this.resultado.setControlArchivoSalida(this.resultado
						.getControlArchivoSalida() + detalle2.getImporte());
			}

			for (String trailerDet : this.trailerDetalle) {
				fout.write(trailerDet + "\n");
			}
			agregarCantidadRegistros();
			fout.write(this.trailer + "\n");

		} catch (IOException e) {
			e.printStackTrace();
			resultado.setEstado(Resultado.ERROR_AL_PROCESAR);
			resultado.setMensajeError("Error al procesar el mensaje");
			return resultado;
		} catch (FormatoFechaException e) {
			e.printStackTrace();
			resultado.setEstado(Resultado.ERROR_AL_PROCESAR);
			resultado.setMensajeError("Error al procesar el mensaje");
			return resultado;
		} catch (ParseoArchivoException e) {
			e.printStackTrace();
			resultado.setEstado(Resultado.ERROR_AL_PROCESAR);
			resultado.setMensajeError("Error al parsear una fila del sistema");
			return resultado;
		} catch (Exception e) {
			e.printStackTrace();
			resultado.setEstado(Resultado.ERROR_AL_PROCESAR);
			return resultado;
		}
		if ((resultado.getControlArchivoEntrada().equals(resultado
				.getControlArchivoSalida()))
				&& resultado.getImporteTotalBaseEntrada().equals(
						resultado.getImporteTotalBaseSalida())) {
			resultado.setEstado(Resultado.PROCESADO_OK);
		} else {
			resultado.setEstado(Resultado.DIFERENCIA_TOTALES);
			resultado
					.setMensajeError("El total del archivo de entrada difiere del total del archivo de salida.");
		}

		return resultado;
	}

	private boolean esTrailerDetalle(String lineaDeArchivo) {

		return "TNNO".equals(lineaDeArchivo.substring(0, 4));
	}

	private void agregarCantidadRegistros() {

		String cantidad = String.valueOf(detalles.size()
				+ detallesNoAgrupadosCantidad);
		cantidad = StringUtilities.toRightAlignedFormat(cantidad, 9, '0')
				.toString();
		this.trailer = this.trailer.substring(0, 36) + cantidad
				+ this.trailer.substring(45);
	}

	private boolean esTrailer(String lineaDeArchivo) {
		return "TFTR".equals(lineaDeArchivo.substring(0, 4));
	}

}
